home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 101-125 / scopedisk109 / ms-dos / src / devio.c < prev    next >
C/C++ Source or Header  |  1995-03-19  |  54KB  |  1,892 lines

  1. /*-
  2.  * $Id: devio.c,v 1.4 90/02/10 21:42:17 Rhialto Exp $
  3.  * $Log:       devio.c,v $
  4.  * Revision 1.4  90/02/10  21:42:17  Rhialto
  5.  * Small changes
  6.  * 
  7.  * Revision 1.3  90/01/27  20:36:04  Rhialto
  8.  * Variable #sectors/track!
  9.  *
  10.  * Revision 1.2  90/01/23  00:41:39  Rhialto
  11.  * Remove C version of DecodeTrack.
  12.  *
  13.  * Revision 1.1  89/12/17  20:04:11  Rhialto
  14.  *
  15.  * DEVIO.C
  16.  *
  17.  * The messydisk.device code that does the real work.
  18.  *
  19.  * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
  20.  * not be used or copied without a licence.
  21. -*/
  22.  
  23. #include "dev.h"
  24. #include "device.h"
  25.  
  26. /*#undef DEBUG                 /**/
  27. #ifdef DEBUG
  28. #   define     debug(x)  dbprintf x
  29. #else
  30. #   define     debug(x)
  31. #endif
  32.  
  33. struct DiskResource *DRResource;/* Argh! A global variable! */
  34. void *CiaBResource;            /* And yet another! */
  35.  
  36. void           Internal_Update();
  37. word           DataCRC();
  38. word           CalculateGapLength();
  39.  
  40. /*-
  41.  *  The high clock bit in this table is still 0, but it could become
  42.  *  a 1 if the two adjecent data bits are both 0.
  43.  *  In fact, that is the principle of MFM clock bits: make sure that no
  44.  *  two 1 bits are adjecent, but not too many (more than 3) 0 bits either.
  45.  *  So, 0 c 0 -> 0 1 0     (where c is a clock bit to be determined)
  46.  *     0 c 1 -> 0 0 1
  47.  *     1 c 0 -> 1 0 0
  48.  *     1 c 1 -> 1 0 1
  49.  *  The sync pattern, $4489, is %0100 0100 1000 1001
  50.  *                               ~ ~  ~ ~  ~ ~  ~ ~ -> %1010 0001 -> $A1
  51.  *  also follows the rules, but won't be formed by encoding $A1...
  52.  *  Since the bytes are written high bit first, the unknown clock bit
  53.  *  (for encoded nybbles 0-7, high bit 0) will become a 1 if the preceding
  54.  *  byte was even (with low bit 0).
  55.  *  So, the clock bit is the NOR of the two data bits.
  56. -*/
  57.  
  58. byte           MfmEncode[16] = {
  59.     0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15,
  60.     0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55
  61. };
  62.  
  63. #define SYNC   0x4489
  64. #define TLEN   12500       /* In BYTES */
  65. #define RLEN   (TLEN+1324) /* 1 sector extra */
  66. #define WLEN   (TLEN+20)   /* 20 bytes more than the theoretical track size */
  67.  
  68. #define INDEXGAP       60  /* All these values are in WORDS */
  69.  
  70. #define IDGAP2         12  /* Sector header: 22 words */
  71. #define IDSYNC          3
  72. #define IDMARK          1
  73. #define IDDATA          4
  74. #define IDCRC           2
  75. #define IDLEN          (IDGAP2+IDSYNC+IDMARK+IDDATA+IDCRC)
  76.  
  77. #define DATAGAP1       22  /* Sector itself: 552 words */
  78. #define DATAGAP2       12
  79. #define DATASYNC        3
  80. #define DATAMARK        1
  81. #define DATACRC         2
  82. #define DATAGAP3_9     78  /* for 9 or less sectors/track */
  83. #define DATAGAP3_10    40  /* for 10 sectors/track */
  84. #define DATALEN        (DATAGAP1+DATAGAP2+DATASYNC+DATAMARK+MS_BPS+DATACRC)
  85.  
  86. #define BLOCKLEN       (IDLEN+DATALEN)     /* Total: 574 words */
  87.  
  88. #define TAILGAP        50
  89.  
  90. /* INDENT OFF */
  91. #asm
  92.  
  93. ; Some hardware data:
  94.  
  95. SYNC           equ $4489
  96. TLEN           equ 12500       ; 2 miscrosecs/bit, 200 ms/track -> 100000 bits
  97. WLEN           equ TLEN+20
  98.  
  99. ;;;;
  100. ;
  101. ;   The following lengths are all in unencoded bytes (or encoded words)
  102.  
  103. INDEXGAP       equ 60
  104.  
  105. IDGAP2         equ 12
  106. IDSYNC         equ  3
  107. IDMARK         equ  1
  108. IDDATA         equ  4
  109. IDCRC          equ  2
  110.  
  111. DATAGAP1       equ 22
  112. DATAGAP2       equ 12
  113. DATASYNC       equ  3
  114. DATAMARK       equ  1
  115. DATACRC        equ  2
  116.  
  117. custom         equ $DFF000
  118.  
  119. Dsklen         equ $24
  120. Intena         equ $9a         ; Interrupt enable  register (write)
  121. Intreq         equ $9c         ; Interrupt request register (write)
  122.  
  123. ; Flags in DSKLEN:
  124.  
  125. dskdmaoff      equ $4000
  126.  
  127. ; Flags in INTENA/INTREQ:
  128.  
  129. intf_setclr    equ 1<<15
  130. intf_dskblk    equ 1<<1
  131.  
  132. ; CIA interrupt control register bits/flags:
  133.  
  134. ciaicrf_flg    equ    1<<4      ; flg interrupt (disk index)
  135.  
  136. ; some cia.resource library functions
  137.  
  138.                public  _LVOSignal
  139.                public  _LVOAbleICR
  140.                public  _LVOSetICR
  141.  
  142. _SafeEnableICR: move.l _CiaBResource,a6
  143.                move.b  4+1(sp),d0
  144.                jsr     _LVOSetICR(a6)      ; clear pending interrupt
  145.                move.b  4+1(sp),d0
  146.                or.b    #$80,d0             ; then enable it
  147.                jsr     _LVOAbleICR(a6)
  148.                rts
  149. ;;;;
  150. ;
  151. ;   Disk index interrupt code.
  152. ;   is_Data (A1) is the value to stuff into the DSKLEN register.
  153. ;   A0 points to the custom chips already.
  154. ;   It then enables the disk block interrupt and disables the
  155. ;   index interrupt.
  156.  
  157. _IndexIntCode:
  158. ;       movem.l A2-A4/D2-D7,-(sp)
  159.        move.w  #dskdmaoff,Dsklen(a0)
  160.        move.w  a1,Dsklen(a0)
  161.        move.w  a1,Dsklen(a0)       ; this enables the DMA
  162.        move.w  #intf_setclr|intf_dskblk,Intena(a0)
  163.        move.l  _CiaBResource,a6
  164.        move.b  #ciaicrf_flg,d0
  165.        jsr     _LVOAbleICR(a6)     ; disable index interrupt
  166. ;       movem.l (sp)+,A2-A4/D2-D7
  167.        rts
  168. ;;;;
  169. ;
  170. ;   Disk DMA finished interrupt code.
  171. ;   (a1) is the task to Signal, 4(a1) is the signal mask to use.
  172. ;   Disables the disk block finished interrupt.
  173.  
  174. _DskBlkIntCode:
  175.        move.w  #dskdmaoff,Dsklen(a0)   ; disable disk DMA
  176.        move.w  #intf_dskblk,Intena(a0) ; disable the interrupt
  177.        move.w  #intf_dskblk,Intreq(a0) ; clear 'diskblock finished' flag
  178.        move.l  4(a1),d0            ; signal mask
  179.        move.l  (a1),a1             ; task to signal
  180.        jsr     _LVOSignal(a6)
  181.        rts
  182.  
  183. #endasm
  184.  
  185. #define DSKDMAEN       (1<<15)
  186. #define DSKWRITE       (1<<14)
  187.  
  188. void IndexIntCode(), DskBlkIntCode();
  189.  
  190. /* INDENT ON */
  191.  
  192. int
  193. HardwareIO(dev, unit, dskwrite)
  194. DEV           *dev;
  195. register UNIT  *unit;
  196. int            dskwrite;
  197. {
  198.     struct {
  199.        struct Task *task;
  200.        ulong signal;
  201.     } tasksig;
  202.  
  203.     debug(("Disk buffer is at %lx\n", dev->md_Rawbuffer));
  204.  
  205.     tasksig.task = FindTask(NULL);
  206.     tasksig.signal = 1L << unit->mu_DmaSignal;
  207.  
  208.     unit->mu_DRUnit.dru_Index.is_Data = (APTR) ((WLEN >> 1)|DSKDMAEN| dskwrite);
  209.     unit->mu_DRUnit.dru_DiscBlock.is_Data = (APTR) &tasksig;
  210.  
  211.     /* Clear signal bit */
  212.     SetSignal(0L, tasksig.signal);
  213.  
  214.     /* Allocate drive and install index and block interrupts */
  215.     GetDrive(&unit->mu_DRUnit);
  216.  
  217.     /* Select correct drive and side */
  218.     ciab.ciaprb = 0xff & ~CIAF_DSKMOTOR;    /* See hardware manual p229 */
  219.     ciab.ciaprb = 0xff & ~CIAF_DSKMOTOR
  220.                       & ~(CIAF_DSKSEL0 << unit->mu_UnitNr)
  221.                       & ~(unit->mu_CurrentSide << CIAB_DSKSIDE);
  222.  
  223.     /* Set up disk parameters */
  224.  
  225. /*
  226.  * This is the adkcon setup: MFM mode, wordsync, no MSBsync, fast mode.
  227.  * The precomp is 0 nanoseconds for the outer half of the disk, 120 for
  228.  * the rest.
  229.  */
  230.     {
  231.        register word adk;
  232.  
  233.        custom.adkcon = ADKF_PRECOMP1|ADKF_PRECOMP0|ADKF_MSBSYNC;
  234.  
  235.        adk = ADKF_SETCLR|ADKF_MFMPREC|ADKF_FAST|ADKF_WORDSYNC;
  236.  
  237.        /* Are we on the inner half ? */
  238.        if (unit->mu_CurrentTrack > unit->mu_NumCyls >> 1) {
  239.            adk |= ADKF_PRECOMP0;
  240.        }
  241.        custom.adkcon = adk;
  242.     }
  243.  
  244.     /* Set up disk buffer address */
  245.     custom.dskpt = (APTR) dev->md_Rawbuffer;
  246.  
  247.     /* Enable disk DMA */
  248.     custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_DISK;
  249.  
  250.     if (dskwrite) {
  251.        /* Enable disk index interrupt to start the whole thing. */
  252.        SafeEnableICR((int) CIAICRF_FLG);
  253.     } else {
  254.        /* Set the sync word */
  255.        custom.dsksync = SYNC;
  256.  
  257.        /* Do the same as the disk index interrupt would */
  258.        custom.dsklen = DSKDMAOFF;
  259.        custom.dsklen = (RLEN >> 1) | DSKDMAEN;
  260.        custom.dsklen = (RLEN >> 1) | DSKDMAEN;
  261.  
  262.        custom.intena = INTF_SETCLR | INTF_DSKBLK;
  263.     }
  264.  
  265.     Wait(tasksig.signal);
  266.  
  267.     FreeDrive();
  268. }
  269.  
  270. #if 0
  271. #define ID_ADDRESS_MARK     0xFE
  272. #define MFM_ID             0x5554
  273. #define DATA_ADDRESS_MARK   0xFB
  274. #define MFM_DATA           0x5545
  275.  
  276. /* INDENT OFF
  277. byte
  278. DecodeByte(mfmdecode, mfm)
  279. byte *mfmdecode;
  280. word mfm;
  281. {
  282.     return mfmdecode[(byte)mfm & 0x7F] |
  283.           mfmdecode[(byte)(mfm >> 8) & 0x7F] << 4;
  284. } */
  285.  
  286. #asm
  287. mfmdecode   set     4
  288. mfm        set     8
  289.  
  290. _DecodeByte:
  291.        move.l  mfmdecode(sp),a0
  292.        move.b  mfm(sp),d1      ; high nybble
  293.        and.w   #$7f,d1         ; strip clock bit (and garbage)
  294.        move.b  (a0,d1.w),d0    ; decode 4 data bits
  295.        lsl.b   #4,d0           ; make room for the rest
  296.  
  297.        move.b  mfm+1(sp),d1    ; low nybble
  298.        and.b   #$7f,d1         ; strip clock bit again
  299.        or.b    (a0,d1.w),d0    ; insert 4 decoded bits
  300.  
  301.        rts
  302.  
  303. #endasm
  304.  
  305. /* INDENT ON */
  306. byte DecodeByte();
  307.  
  308. int
  309. DecodeTrack(dev, unit)
  310. DEV           *dev;
  311. UNIT          *unit;
  312. {
  313.     register word  *rawbuf = (word *)dev->md_Rawbuffer; /*  a2 */
  314.     byte          *rawend = (byte *)rawbuf + RLEN - (MS_BPS+2)*sizeof(word);
  315.     byte          *trackbuf = unit->mu_TrackBuffer;
  316.     register byte  *decode = dev->md_MfmDecode;        /*  a3 */
  317.     word          *oldcrc = unit->mu_CrcBuffer;
  318.     register byte  *secptr;                            /*  a4 */
  319.     long           sector;
  320.     word           numsecs;
  321.     register long   numbytes;                          /*  d3 */
  322.     word           maxsec;
  323.  
  324. #define Len    ((byte *)rawbuf - dev->md_Rawbuffer)
  325.     maxsec = 0;
  326.  
  327.     for (numsecs = 0; numsecs < MS_SPT_MAX; numsecs++) {
  328.        /*
  329.         *  First try to find a sector id.
  330.         */
  331. find_id:
  332.        while (*rawbuf != SYNC) {
  333.            if (++rawbuf >= rawend) {
  334.                debug(("id start, EOT %4x\n", Len));
  335.                goto end;
  336.            }
  337.        }
  338.        while (*rawbuf == SYNC) {
  339.            rawbuf++;
  340.        }
  341.        if (*rawbuf++ != MFM_ID) {
  342.            debug(("No ID (%4x), %4x\n", rawbuf[-1], Len));
  343.            goto find_id;
  344.        }
  345.  
  346.        sector = DecodeByte(decode, *rawbuf++);
  347.        if (sector != unit->mu_CurrentTrack) {
  348.            debug(("Track error?? %d\n", (int)sector));
  349.            goto find_id;
  350.        }
  351.        sector = DecodeByte(decode, *rawbuf++);
  352.        if (sector != unit->mu_CurrentSide) {
  353.            debug(("Side error?? %d\n", (int)sector));
  354.            goto find_id;
  355.        }
  356.        if (rawbuf >= rawend) {
  357.            debug(("id end, EOT %4x\n", Len));
  358.            goto end;
  359.        }
  360.        sector = DecodeByte(decode, *rawbuf++);
  361.        debug(("#%2d %4x, ", (int)sector, Len-0xC));
  362.        if (sector > MS_SPT_MAX) {
  363.            debug(("Bogus sector number) "));
  364.            goto find_id;
  365.        }
  366.        if (sector > maxsec)
  367.            maxsec = sector;
  368.        sector--;               /* Normalize sector number */
  369.  
  370.        /*
  371.         *  Then find the data block.
  372.         */
  373. find_data:
  374.        while (*rawbuf != SYNC) {
  375.            if (++rawbuf >= rawend) {
  376.                debug(("data start, EOT %4x\n", Len));
  377.                return 0; /* TDERR_TooFewSecs; */
  378.            }
  379.        }
  380.        while (*rawbuf == SYNC) {
  381.            rawbuf++;
  382.        }
  383.        if (*rawbuf++ != MFM_DATA) {
  384.            debug(("No Data (%4x), %4x\n", rawbuf[-1], Len));
  385.            goto find_id;
  386.        }
  387.        debug(("%4x, ", Len-8));
  388.  
  389.        if (rawbuf >= rawend) {
  390.            debug(("short data, EOT %4x\n", Len));
  391.            goto end;
  392.        }
  393.        secptr = trackbuf + MS_BPS * sector;
  394.        for (numbytes = 0; numbytes < MS_BPS; numbytes++) {
  395.            *secptr++ = DecodeByte(decode, *rawbuf++);
  396.        }
  397.        debug(("%4x\n", Len));
  398.        oldcrc[sector]  = DecodeByte(decode, *rawbuf++) << 8;
  399.        oldcrc[sector] |= DecodeByte(decode, *rawbuf++);
  400.        unit->mu_SectorStatus[sector] = unit->mu_InitSectorStatus;
  401.     }
  402.  
  403. end:
  404.     if (numsecs == 0)
  405.        return TDERR_TooFewSecs;
  406.  
  407. #ifndef READONLY
  408.     /*
  409.      * If we read the very first track, we adjust our notion about the
  410.      * number of sectors on each track. This is the only track we can
  411.      * accurately find if this number is unknown. Let's hope that the first
  412.      * user of this disk starts reading it here.
  413.      */
  414.     if (unit->mu_CurrentTrack == 0 && unit->mu_CurrentSide == 0) {
  415.        unit->mu_SectorsPerTrack = maxsec;
  416.     }
  417.     unit->mu_CurrentSectors = maxsec;
  418.     debug(("%d sectors\n", unit->mu_SectorsPerTrack));
  419. #endif
  420.  
  421.     return 0;
  422.  
  423. #undef Len
  424. }
  425. #else  /* Use assembly */
  426.  
  427. int
  428. DecodeTrack(dev, unit)
  429. DEV           *dev;
  430. UNIT          *unit;
  431. {
  432.     register word  *rawbuf = (word *)dev->md_Rawbuffer; /*  a2 */
  433.     byte          *rawend = (byte *)rawbuf + RLEN - (MS_BPS+2)*sizeof(word);
  434.     byte          *trackbuf = unit->mu_TrackBuffer;
  435.     register byte  *decode = dev->md_MfmDecode;        /*  a3 */
  436.     word          *oldcrc = unit->mu_CrcBuffer;
  437.     register byte  *secptr;                            /*  a4 */
  438.     long           sector;
  439.     word           numsecs;
  440.     register long   numbytes;                          /*  d3 */
  441.     word           maxsec;
  442.  
  443. #asm
  444.  
  445. MFM_ID     equ     $5554
  446. MFM_DATA    equ     $5545
  447.  
  448. rawbuf     equr    a2
  449. decode     equr    a3
  450. secptr     equr    a4
  451. numbytes    equr    d3
  452.  
  453. rawend     set     -4
  454. trackbuf    set     -8
  455. oldcrc     set     -12
  456. sector     set     -16
  457. numsecs     set     -18
  458. maxsec     set     -20
  459.  
  460.        move.w  #0,numsecs(a5)      ; no sectors found yet
  461.        move.w  #0,maxsec(a5)       ; and no highest sector number
  462.  
  463. ;;;;   First we will try to find a sector id.
  464. find_id:
  465.        cmp     #SYNC,(rawbuf)+
  466.        beq.s   fid_gotsync
  467.        cmpa.l  rawend(a5),rawbuf
  468.        blt     find_id
  469.        bra     return              ; We ran off the end of the buffer.
  470.  
  471. fid_gotsync:                       ; Skip the other syncs.
  472.        cmp.w   #SYNC,(rawbuf)
  473.        bne     fid_endsync
  474.        lea     2(rawbuf),rawbuf
  475.        bra     fid_gotsync
  476.  
  477. fid_endsync:
  478.        cmp.w   #MFM_ID,(rawbuf)+
  479.        bne     find_id
  480.  
  481.        bsr     DecodeByte          ; track #
  482.        bsr     DecodeByte          ; side #
  483.        moveq.l #0,d0               ; clear high part
  484.        bsr     DecodeByte          ; sector #
  485.        cmp.w   #MS_SPT_MAX,d0      ; sector number too large?
  486.        bgt     find_id
  487.        cmp.w   maxsec(a5),d0       ; what is the highest sector number?
  488.        ble     nomax
  489.        move.w  d0,maxsec(a5)       ; record the highest sector number
  490. nomax:
  491.        subq.w  #1,d0               ; normalize sector number
  492.        move.l  d0,sector(a5)
  493.  
  494. find_data:                         ; Then find the data block.
  495.        cmp     #SYNC,(rawbuf)+
  496.        beq.s   fda_gotsync
  497.        cmpa.l  rawend(a5),rawbuf
  498.        blt     find_data
  499.        bra     return              ; we ran off the end of the buffer.
  500.  
  501. fda_gotsync:                       ; skip the other syncs.
  502.        cmp.w   #SYNC,(rawbuf)
  503.        bne     fda_endsync
  504.        lea     2(rawbuf),rawbuf
  505.        bra     fda_gotsync
  506.  
  507. fda_endsync:
  508.        cmp.w   #MFM_DATA,(rawbuf)+ ; do we really have a data block?
  509.        bne     find_id
  510.  
  511.        cmpa.l  rawend(a5),rawbuf   ; will we still be inside the mfm data?
  512.        bge     return
  513.  
  514.        move.l  sector(a5),d0       ; calculate the location to
  515.        moveq.l #LOG2_MS_BPS,d1     ;  store this sector.
  516.        asl.l   d1,d0
  517.        move.l  trackbuf(a5),secptr
  518.        add.l   d0,secptr
  519.  
  520.        move.w  #MS_BPS-1,numbytes
  521. data_copy:
  522.        bsr     DecodeByte
  523.        move.b  d0,(secptr)+
  524.        dbra    numbytes,data_copy
  525.  
  526.        move.l  sector(a5),d3       ; get pointer to crc location
  527.        add.l   d3,d3               ; 2 bytes of crc per sector
  528.        move.l  oldcrc(a5),a0
  529.        add.l   d3,a0
  530.  
  531.        bsr     DecodeByte          ; get high byte
  532.        move.b  d0,(a0)+
  533.        bsr     DecodeByte          ; and low byte of crc
  534.        move.b  d0,(a0)+
  535.  
  536. #endasm
  537.        unit->mu_SectorStatus[sector] = unit->mu_InitSectorStatus;
  538. #asm
  539.        addq.w  #1,numsecs(a5)
  540.        cmp.w   #MS_SPT_MAX,numsecs(a5)
  541.        blt     find_id
  542. return:
  543. #endasm
  544.  
  545.     if (numsecs == 0)
  546.        return TDERR_TooFewSecs;
  547.  
  548. #ifndef READONLY
  549.     /*
  550.      * If we read the very first track, we adjust our notion about the
  551.      * number of sectors on each track. This is the only track we can
  552.      * accurately find if this number is unknown. Let's hope that the first
  553.      * user of this disk starts reading it here.
  554.      */
  555.     if (unit->mu_CurrentTrack == 0 && unit->mu_CurrentSide == 0) {
  556.        unit->mu_SectorsPerTrack = maxsec;
  557.     }
  558.     unit->mu_CurrentSectors = maxsec;
  559.     debug(("%d sectors\n", unit->mu_SectorsPerTrack));
  560. #endif
  561.  
  562.     return 0;
  563.  
  564. }
  565.  
  566. #asm
  567. ;;;;
  568. ;
  569. ;   Decode a single MFM word to a byte.
  570. ;   Auto-increments the rawbuffer pointer.
  571.  
  572. DecodeByte:
  573.        move.b  (rawbuf)+,d1    ; high nybble
  574.        and.w   #$7f,d1         ; strip clock bit (and garbage)
  575.        move.b  (decode,d1.w),d0; decode 4 data bits
  576.        lsl.b   #4,d0           ; make room for the rest
  577.  
  578.        move.b  (rawbuf)+,d1    ; low nybble
  579.        and.b   #$7f,d1         ; strip clock bit again
  580.        or.b    (decode,d1.w),d0; insert 4 decoded bits
  581.  
  582.        rts
  583.  
  584. #endasm
  585. #endif /* using assembly */
  586.  
  587. /*
  588.  * Initialize the ibm mfm decoding table
  589.  */
  590.  
  591. void
  592. InitDecoding(decode)
  593. register byte  *decode;
  594. {
  595.     register int    i;
  596.  
  597.     i = 0;
  598.     do {
  599.        decode[i] = 0xff;
  600.     } while (++i < 128);
  601.  
  602.     i = 0;
  603.     do {
  604.        decode[MfmEncode[i]] = i;
  605.     } while (++i < 0x10);
  606. }
  607.  
  608. #ifdef notdef
  609. long
  610. MyDoIO(req)
  611. register struct IORequest *req;
  612. {
  613.     req->io_Flags |= IOF_QUICK;
  614.     BeginIO(req);
  615.     return WaitIO(req);
  616. }
  617. #endif
  618.  
  619. /*
  620.  * Switch the drive motor on. Return previous state. Don't use this when
  621.  * you have allocated the disk via GetDrive().
  622.  */
  623.  
  624. int
  625. TDMotorOn(tdreq)
  626. register struct IOExtTD *tdreq;
  627. {
  628.     debug(("TDMotorOn "));
  629.     tdreq->iotd_Req.io_Command = TD_MOTOR;
  630.     tdreq->iotd_Req.io_Length = 1;
  631.     DoIO(tdreq);
  632.     debug(("was %ld\n", tdreq->iotd_Req.io_Actual));
  633.  
  634.     return tdreq->iotd_Req.io_Actual;
  635. }
  636.  
  637. /*
  638.  * Get the number of cylinders the drive is capable of using.
  639.  */
  640.  
  641. int
  642. TDGetNumCyls(tdreq)
  643. register struct IOExtTD *tdreq;
  644. {
  645.     tdreq->iotd_Req.io_Command = TD_GETNUMTRACKS;
  646.     DoIO(tdreq);
  647.  
  648.     return tdreq->iotd_Req.io_Actual / NUMHEADS;
  649. }
  650.  
  651. /*
  652.  * Seek the drive to the indicated cylinder. Use the trackdisk.device for
  653.  * ease. Don't use this when you have allocated the disk via GetDrive().
  654.  */
  655.  
  656. int
  657. TDSeek(unit, ioreq, cylinder)
  658. UNIT          *unit;
  659. struct IOStdReq *ioreq;
  660. int            cylinder;
  661. {
  662.     register struct IOExtTD *tdreq = unit->mu_DiskIOReq;
  663.  
  664.     debug(("TDSeek %d\n", cylinder));
  665.  
  666.     tdreq->iotd_Req.io_Command = TD_SEEK;
  667.     tdreq->iotd_Req.io_Offset = cylinder * (TD_SECTOR * NUMSECS * NUMHEADS);
  668.     if ((ioreq->io_Flags & IOMDF_40TRACKS) && (unit->mu_NumCyls == 80))
  669.        tdreq->iotd_Req.io_Offset *= 2;
  670.     DoIO(tdreq);
  671.  
  672.     return tdreq->iotd_Req.io_Error;
  673. }
  674.  
  675. void          *
  676. GetDrive(drunit)
  677. register struct DiskResourceUnit *drunit;
  678. {
  679.     register void  *LastDriver;
  680.  
  681.     debug(("GetDrive: "));
  682.     for (;;) {
  683.        drunit->dru_Message.mn_Node.ln_Type = NT_MESSAGE;
  684.        LastDriver = GetUnit(drunit);
  685.  
  686.        debug(("LastDriver %08lx\n", LastDriver));
  687.        if (LastDriver) {
  688.            return LastDriver;
  689.        } else {
  690.            while (drunit->dru_Message.mn_Node.ln_Type != NT_REPLYMSG)
  691.                Wait(1L << drunit->dru_Message.mn_ReplyPort->mp_SigBit);
  692.            Remove(drunit);
  693.            debug(("GetDrive: Retry\n"));
  694.        }
  695.     }
  696. }
  697.  
  698. void
  699. FreeDrive()
  700. {
  701.     GiveUnit();
  702. }
  703.  
  704. int
  705. GetTrack(ioreq, side, track)
  706. struct IOStdReq *ioreq;
  707. int            side;
  708. int            track;
  709. {
  710.     register int    i;
  711.     DEV           *dev;
  712.     register UNIT  *unit;
  713.  
  714.     debug(("GetTrack %d %d\n", track, side));
  715.     dev = (DEV *) ioreq->io_Device;
  716.     unit = (UNIT *) ioreq->io_Unit;
  717.  
  718.     if (track != unit->mu_CurrentTrack || side != unit->mu_CurrentSide) {
  719. #ifndef READONLY
  720.        Internal_Update(ioreq, unit);
  721. #endif
  722.        for (i = MS_SPT_MAX-1; i >= 0; i--) {
  723.            unit->mu_SectorStatus[i] = TDERR_NoSecHdr;
  724.        }
  725.  
  726.        TDMotorOn(unit->mu_DiskIOReq);
  727.        if (TDSeek(unit, ioreq, track)) {
  728.            debug(("Seek error\n"));
  729.            return ioreq->io_Error = IOERR_BADLENGTH;
  730.        }
  731.        unit->mu_CurrentTrack = track;
  732.        unit->mu_CurrentSide = side;
  733.        ObtainSemaphore(&dev->md_HardwareUse);
  734.        HardwareIO(dev, unit, 0);
  735.        i = DecodeTrack(dev, unit);
  736.        ReleaseSemaphore(&dev->md_HardwareUse);
  737.        debug(("DecodeTrack returns %d\n", i));
  738.  
  739.        if (i != 0) {
  740.            unit->mu_CurrentTrack = -1;
  741.            return i;
  742.        }
  743.     }
  744.  
  745.     return 0;
  746. }
  747.  
  748. /*
  749.  * Test if it is changed
  750.  */
  751.  
  752. int
  753. CheckChanged(ioreq, unit)
  754. struct IOExtTD *ioreq;
  755. register UNIT  *unit;
  756. {
  757.     register struct IOExtTD *tdreq;
  758.  
  759.     if ((ioreq->iotd_Req.io_Command & TDF_EXTCOM) &&
  760.        ioreq->iotd_Count < unit->mu_ChangeNum) {
  761. diskchanged:
  762.        ioreq->iotd_Req.io_Error = TDERR_DiskChanged;
  763. error:
  764.        return 1;
  765.     }
  766.     return 0;
  767. }
  768.  
  769. /*
  770.  * Test if we can read or write the disk. Is it inserted and writable?
  771.  */
  772.  
  773. int
  774. CheckRequest(ioreq, unit)
  775. struct IOExtTD *ioreq;
  776. register UNIT  *unit;
  777. {
  778.     register struct IOExtTD *tdreq;
  779.  
  780.     if ((ioreq->iotd_Req.io_Command & TDF_EXTCOM) &&
  781.        ioreq->iotd_Count < unit->mu_ChangeNum) {
  782. diskchanged:
  783.        ioreq->iotd_Req.io_Error = TDERR_DiskChanged;
  784. error:
  785.        return 1;
  786.     }
  787.     /*
  788.      * if (ioreq->iotd_Req.io_Offset + ioreq->iotd_Req.io_Length >
  789.      * (unit->mu_NumCyls * MS_NSIDES * MS_SPT * MS_BPS)) {
  790.      * ioreq->iotd_Req.io_Error = IOERR_BADLENGTH; goto error; }
  791.      */
  792.  
  793.     tdreq = unit->mu_DiskIOReq;
  794.  
  795.     if (unit->mu_DiskState == STATEF_UNKNOWN) {
  796.        tdreq->iotd_Req.io_Command = TD_PROTSTATUS;
  797.        DoIO(tdreq);
  798.        if (tdreq->iotd_Req.io_Error == 0) {
  799.            if (tdreq->iotd_Req.io_Actual == 0) {
  800.                unit->mu_DiskState = STATEF_PRESENT | STATEF_WRITABLE;
  801.            } else
  802.                unit->mu_DiskState = STATEF_PRESENT;
  803.        } else
  804.            unit->mu_DiskState = 0;
  805.     }
  806.     if (!(unit->mu_DiskState & STATEF_PRESENT))
  807.        goto diskchanged;
  808.  
  809.     /*
  810.      * Check _WRITE, _UPDATE, _FORMAT
  811.      */
  812.     if (STRIP(ioreq->iotd_Req.io_Command) != CMD_READ) {
  813.        if (!(unit->mu_DiskState & STATEF_WRITABLE)) {
  814.            ioreq->iotd_Req.io_Error = TDERR_WriteProt;
  815.            goto error;
  816.        }
  817.     }
  818.     return 0;
  819. }
  820.  
  821.  
  822. /*
  823.  * Read zero or more sectors from the disk and copy them into the user's
  824.  * buffer.
  825.  */
  826.  
  827. void
  828. CMD_Read(ioreq, unit)
  829. register struct IOExtTD *ioreq;
  830. register UNIT  *unit;
  831. {
  832.     int            side;
  833.     int            cylinder;
  834.     int            sector;
  835.     byte          *userbuf;
  836.     long           length;
  837.     long           offset;
  838.     byte          *diskbuf;
  839.     int            retrycount;
  840.  
  841.     debug(("CMD_Read "));
  842.     userbuf = (byte *) ioreq->iotd_Req.io_Data;
  843.     length = ioreq->iotd_Req.io_Length / MS_BPS;       /* Sector count */
  844.     offset = ioreq->iotd_Req.io_Offset / MS_BPS;       /* Sector number */
  845.     debug(("userbuf %08lx off %ld len %ld ", userbuf, offset, length));
  846.  
  847.     cylinder = offset / unit->mu_SectorsPerTrack;
  848.     side = cylinder % MS_NSIDES;
  849.     cylinder /= MS_NSIDES;
  850.     sector = offset % unit->mu_SectorsPerTrack;       /* 0..8 or 9 */
  851.     debug(("Tr=%d Si=%d Se=%d\n", cylinder, side, sector));
  852.  
  853.     ioreq->iotd_Req.io_Actual = 0;
  854.  
  855.     if (length <= 0 || CheckRequest(ioreq, unit))
  856.        goto end;
  857.  
  858.     retrycount = 0;
  859.     diskbuf = unit->mu_TrackBuffer + MS_BPS * sector;
  860. gettrack:
  861.     GetTrack(ioreq, side, cylinder);
  862.  
  863.     for (;;) {
  864.        /*
  865.         * Have we ever checked this CRC?
  866.         */
  867.        if (unit->mu_SectorStatus[sector] == CRC_UNCHECKED) {
  868.            /*
  869.             * Do it now. If it mismatches, remember that for later.
  870.             */
  871.            if (unit->mu_CrcBuffer[sector] != DataCRC(diskbuf)) {
  872.                debug(("%d: %04x, now %04x\n", sector, unit->mu_CrcBuffer[sector], DataCRC(diskbuf)));
  873.                unit->mu_SectorStatus[sector] = TDERR_BadSecSum;
  874.            } else
  875.                unit->mu_SectorStatus[sector] = TDERR_NoError;
  876.        }
  877.        if (unit->mu_SectorStatus[sector] > TDERR_NoError) {
  878.            if (++retrycount < 3) {
  879.                unit->mu_CurrentTrack = -1;
  880.                goto gettrack;
  881.            }
  882.            ioreq->iotd_Req.io_Error = unit->mu_SectorStatus[sector];
  883.            goto end;       /* Don't use this sector anymore... */
  884.        }
  885.        retrycount = 0;
  886.        CopyMem(diskbuf, userbuf, (long) MS_BPS);
  887.        ioreq->iotd_Req.io_Actual += MS_BPS;
  888.        if (--length <= 0)
  889.            break;
  890.        userbuf += MS_BPS;
  891.        diskbuf += MS_BPS;
  892.        if (++sector >= unit->mu_SectorsPerTrack) {
  893.            sector = 0;
  894.            diskbuf = unit->mu_TrackBuffer;
  895.            if (++side >= MS_NSIDES) {
  896.                side = 0;
  897.                if (++cylinder >= unit->mu_NumCyls) {
  898.                    /* ioreq->iotd_Req.io_Error = IOERR_BADLENGTH; */
  899.                    goto end;
  900.                }
  901.            }
  902.            GetTrack(ioreq, side, cylinder);
  903.        }
  904.     }
  905.  
  906. end:
  907.     TermIO(ioreq);
  908. }
  909.  
  910. #ifdef READONLY
  911.  
  912. void
  913. CMD_Write(ioreq, unit)
  914. register struct IOExtTD *ioreq;
  915. UNIT          *unit;
  916. {
  917.     ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
  918.     TermIO(ioreq);
  919. }
  920.  
  921. void
  922. TD_Format(ioreq, unit)
  923. register struct IOExtTD *ioreq;
  924. UNIT          *unit;
  925. {
  926.     ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
  927.     TermIO(ioreq);
  928. }
  929.  
  930. #endif
  931.  
  932. void
  933. CMD_Reset(ioreq, unit)
  934. struct IOExtTD *ioreq;
  935. UNIT          *unit;
  936. {
  937.     unit->mu_CurrentSide = -1;
  938.     unit->mu_TrackChanged = 0;
  939.     TermIO(ioreq);
  940. }
  941.  
  942. void
  943. CMD_Update(ioreq, unit)
  944. struct IOExtTD *ioreq;
  945. register UNIT  *unit;
  946. {
  947. #ifndef READONLY
  948.     if (unit->mu_TrackChanged && !CheckRequest(ioreq, unit))
  949.        Internal_Update(ioreq, unit);
  950. #endif
  951.     TermIO(ioreq);
  952. }
  953.  
  954. void
  955. CMD_Clear(ioreq, unit)
  956. struct IOExtTD *ioreq;
  957. UNIT          *unit;
  958. {
  959.     if (!CheckChanged(ioreq, unit)) {
  960.        unit->mu_CurrentSide = -1;
  961.        unit->mu_TrackChanged = 0;
  962.     }
  963.     TermIO(ioreq);
  964. }
  965.  
  966. void
  967. TD_Seek(ioreq, unit)
  968. struct IOExtTD *ioreq;
  969. UNIT          *unit;
  970. {
  971.     if (!CheckChanged(ioreq, unit)) {
  972.        word            cylinder;
  973.  
  974.        cylinder = (ioreq->iotd_Req.io_Offset / unit->mu_SectorsPerTrack) /
  975.                    (MS_BPS * MS_NSIDES);
  976.        TDSeek(unit, ioreq, cylinder);
  977.     }
  978.     TermIO(ioreq);
  979. }
  980.  
  981. /*
  982.  * Ask the trackdisk.device for the answer, but keep a local copy.
  983.  */
  984.  
  985. void
  986. TD_Changenum(ioreq, unit)
  987. struct IOExtTD *ioreq;
  988. UNIT          *unit;
  989. {
  990.     register struct IOStdReq *req;
  991.  
  992.     req = &unit->mu_DiskIOReq->iotd_Req;
  993.     req->io_Command = TD_CHANGENUM;
  994.     DoIO(req);
  995.  
  996.     unit->mu_ChangeNum = req->io_Actual;
  997.     ioreq->iotd_Req.io_Actual = req->io_Actual;
  998.     TermIO(ioreq);
  999. }
  1000.  
  1001. int
  1002. DevInit(dev)
  1003. register DEV   *dev;
  1004. {
  1005.     if (!(DRResource = OpenResource(DISKNAME)))
  1006.        goto abort;
  1007.  
  1008.     if (!(CiaBResource = OpenResource(CIABNAME)))
  1009.        goto abort;
  1010.  
  1011. #ifndef READONLY
  1012.     if (!InitWrite(dev))
  1013.        goto abort;
  1014. #endif
  1015.  
  1016.     InitDecoding(dev->md_MfmDecode);
  1017.     InitSemaphore(&dev->md_HardwareUse);
  1018.     return 1;                  /* Initializing succeeded */
  1019.  
  1020. abort:
  1021.     return DevCloseDown(dev);
  1022. }
  1023.  
  1024. int
  1025. DevCloseDown(dev)
  1026. DEV           *dev;
  1027. {
  1028. #ifndef READONLY
  1029.     FreeBuffer(dev);
  1030. #endif
  1031.     return 0;                  /* Now unitialized */
  1032. }
  1033.  
  1034. #ifndef READONLY
  1035. /*
  1036.  * Calculate the length between the sectors, given the length of the track
  1037.  * and the number of sectors that must fit on it.
  1038.  * The proper formula would be
  1039.  * (((TLEN/2) - INDEXGAP - TAILGAP) / unit->mu_SectorsPerTrack) - BLOCKLEN;
  1040.  */
  1041.  
  1042. word
  1043. CalculateGapLength(sectors)
  1044. int            sectors;
  1045. {
  1046.     return (sectors == 10) ? DATAGAP3_10 : DATAGAP3_9;
  1047. }
  1048. #endif
  1049.  
  1050. UNIT          *
  1051. UnitInit(dev, UnitNr)
  1052. DEV           *dev;
  1053. ulong          UnitNr;
  1054. {
  1055.     register UNIT  *unit;
  1056.     struct Task    *task;
  1057.     struct IOStdReq *dcr;
  1058.     struct IOExtTD *tdreq;
  1059.  
  1060.     unit = AllocMem((long) sizeof (UNIT), MEMF_PUBLIC | MEMF_CLEAR);
  1061.     if (unit == NULL)
  1062.        return NULL;
  1063.  
  1064.     if (!(tdreq = CreateExtIO(&unit->mu_DiskReplyPort, (long) sizeof (*tdreq)))) {
  1065.        goto abort;
  1066.     }
  1067.     unit->mu_DiskIOReq = tdreq;
  1068.     if (OpenDevice(TD_NAME, UnitNr, tdreq, TDF_ALLOW_NON_3_5)) {
  1069.        tdreq->iotd_Req.io_Device = NULL;
  1070.        goto abort;
  1071.     }
  1072.     dcr = (void *) CreateExtIO(&unit->mu_DiskReplyPort, (long) sizeof (*dcr));
  1073.     if (dcr) {
  1074.        unit->mu_DiskChangeReq = dcr;
  1075.        unit->mu_DiskChangeInt.is_Node.ln_Pri = 32;
  1076.        unit->mu_DiskChangeInt.is_Data = (APTR) unit;
  1077.        unit->mu_DiskChangeInt.is_Code = DiskChangeHandler;
  1078.        /* Clone IO request part */
  1079.        dcr->io_Device = tdreq->iotd_Req.io_Device;
  1080.        dcr->io_Unit = tdreq->iotd_Req.io_Unit;
  1081.        dcr->io_Command = TD_ADDCHANGEINT;
  1082.        dcr->io_Data = (void *) &unit->mu_DiskChangeInt;
  1083.        SendIO(dcr);
  1084.     }
  1085.     NewList(&unit->mu_ChangeIntList);
  1086.  
  1087.     unit->mu_NumCyls = TDGetNumCyls(tdreq);
  1088.     unit->mu_UnitNr = UnitNr;
  1089.     unit->mu_DiskState = STATEF_UNKNOWN;
  1090.     unit->mu_TrackChanged = 0;
  1091.     unit->mu_CurrentSide = -1;
  1092.     unit->mu_InitSectorStatus = CRC_UNCHECKED;
  1093.     unit->mu_SectorsPerTrack = MS_SPT;
  1094.  
  1095.     unit->mu_DRUnit.dru_Message.mn_ReplyPort = &unit->mu_DiskReplyPort;
  1096.     unit->mu_DRUnit.dru_Index.is_Node.ln_Pri = 32; /* high pri for index int */
  1097.     unit->mu_DRUnit.dru_Index.is_Code = IndexIntCode;
  1098.     unit->mu_DRUnit.dru_DiscBlock.is_Code = DskBlkIntCode;
  1099.  
  1100.     /*
  1101.      * Now create the Unit task. Remember that it won't start running
  1102.      * since we are Forbid()den. But just to be sure, we Forbid() again.
  1103.      */
  1104.     Forbid();
  1105.     task = CreateTask(DevName, TASKPRI, UnitTask, TASKSTACK);
  1106.     task->tc_UserData = (APTR) unit;
  1107.  
  1108.     unit->mu_Port.mp_Flags = PA_IGNORE;
  1109.     unit->mu_Port.mp_SigTask = task;
  1110.     NewList(&unit->mu_Port.mp_MsgList);
  1111.  
  1112.     unit->mu_DiskReplyPort.mp_Flags = PA_IGNORE;
  1113.     unit->mu_DiskReplyPort.mp_SigTask = task;
  1114.     NewList(&unit->mu_DiskReplyPort.mp_MsgList);
  1115.     Permit();
  1116.  
  1117.     return unit;
  1118.  
  1119. abort:
  1120.     UnitCloseDown(NULL, dev, unit);
  1121.     return NULL;
  1122. }
  1123.  
  1124. int
  1125. UnitCloseDown(ioreq, dev, unit)
  1126. struct IOExtTD *ioreq;
  1127. DEV           *dev;
  1128. register UNIT  *unit;
  1129. {
  1130. #ifndef READONLY
  1131.     if (ioreq && unit->mu_TrackChanged)
  1132.        Internal_Update(ioreq, unit);
  1133. #endif
  1134.  
  1135.     /*
  1136.      * Get rid of the Unit's task. We know this is safe because the unit
  1137.      * has an open count of zero, so it is 'guaranteed' not in use.
  1138.      */
  1139.  
  1140.     if (unit->mu_Port.mp_SigTask) {
  1141. #ifdef DEBUG
  1142.        extern struct SignalSemaphore PortUse;
  1143.  
  1144.        /*
  1145.         * Make sure that the unit task does not get removed when it has
  1146.         * the semaphore.
  1147.         */
  1148.        ObtainSemaphore(&PortUse);
  1149. #endif
  1150.        RemTask(unit->mu_Port.mp_SigTask);
  1151. #ifdef DEBUG
  1152.        ReleaseSemaphore(&PortUse);
  1153. #endif
  1154.     }
  1155.     if (unit->mu_DiskChangeReq) {
  1156. #if 0                          /* V1.2 and V1.3 have a broken
  1157.                                 * TD_REMCHANGEINT */
  1158.        register struct IOExtTD *req = unit->mu_DiskIOReq;
  1159.  
  1160.        req->iotd_Req.io_Command = TD_REMCHANGEINT;
  1161.        req->iotd_Req.io_Data = (void *) unit->mu_DiskChangeReq;
  1162.        DoIO(req);
  1163.        WaitIO(unit->mu_DiskChangeReq);
  1164. #else
  1165.        Disable();
  1166.        Remove(unit->mu_DiskChangeReq);
  1167.        Enable();
  1168. #endif
  1169.        DeleteExtIO(unit->mu_DiskChangeReq);
  1170.        unit->mu_DiskChangeReq = NULL;
  1171.     }
  1172.     if (unit->mu_DiskIOReq) {
  1173.        if (unit->mu_DiskIOReq->iotd_Req.io_Device)
  1174.            CloseDevice(unit->mu_DiskIOReq);
  1175.        DeleteExtIO(unit->mu_DiskIOReq);
  1176.        unit->mu_DiskIOReq = NULL;
  1177.     }
  1178.     FreeMem(unit, (long) sizeof (UNIT));
  1179.  
  1180.     return 0;                  /* Now unitialized */
  1181. }
  1182.  
  1183. /*
  1184.  * Create missing bindings
  1185.  */
  1186. /* INDENT OFF */
  1187.  
  1188. #asm
  1189.  
  1190. lib_vectsize   equ     6
  1191. lib_base       equ     -lib_vectsize
  1192.  
  1193. _RVOAllocUnit  equ     lib_base-(0*lib_vectsize)
  1194. _RVOFreeUnit   equ     lib_base-(1*lib_vectsize)
  1195. _RVOGetUnit    equ     lib_base-(2*lib_vectsize)
  1196. _RVOGiveUnit   equ     lib_base-(3*lib_vectsize)
  1197. _RVOGetUnitID  equ     lib_base-(4*lib_vectsize)
  1198.  
  1199. ;_AllocUnit:
  1200. ;              move.l  _DRResource,a6
  1201. ;              move.l  4(sp),d0
  1202. ;              jmp     _RVOAllocUnit(a6)
  1203. ;_FreeUnit:
  1204. ;              move.l  _DRResource,a6
  1205. ;              move.l  4(sp),d0
  1206. ;              jmp     _RVOFreeUnit(a6)
  1207. _GetUnit:
  1208.                move.l  _DRResource,a6
  1209.                move.l  4(sp),a1
  1210.                jmp     _RVOGetUnit(a6)
  1211. ;_GetUnitID:
  1212. ;              move.l  _DRResource,a6
  1213. ;              move.l  4(sp),d0
  1214. ;              jmp     _RVOGetUnitID(a6)
  1215. _GiveUnit:
  1216.                move.l  _DRResource,a6
  1217.                jmp     _RVOGiveUnit(a6)
  1218.  
  1219. #endasm
  1220. /* INDENT ON */
  1221.  
  1222. /*
  1223.  * We handle disk change interrupts internally, since the io request is
  1224.  * held by the device. Since SoftInts caused by the trackdisk.device are
  1225.  * broadcast to our clients, our own softint must have the highest
  1226.  * priority possible.
  1227.  *
  1228.  * TD_Addchangeint is an IMMEDIATE command, so no exclusive use of the list
  1229.  * is acquired (nor released). The list is accessed by (software)
  1230.  * interrupt code.
  1231.  */
  1232.  
  1233. void
  1234. TD_Addchangeint(ioreq)
  1235. register struct IOStdReq *ioreq;
  1236. {
  1237.     register UNIT  *unit;
  1238.  
  1239.     unit = (UNIT *) ioreq->io_Unit;
  1240.     Disable();
  1241.     AddTail(&unit->mu_ChangeIntList, ioreq);
  1242.     Enable();
  1243.     ioreq->io_Flags &= ~IOF_QUICK;     /* So we call ReplyMsg instead of
  1244.                                         * TermIO */
  1245.     /* Note no TermIO */
  1246. }
  1247.  
  1248. void
  1249. TD_Remchangeint(ioreq)
  1250. register struct IOStdReq *ioreq;
  1251. {
  1252.     register struct IOStdReq *intreq;
  1253.  
  1254.     intreq = (struct IOStdReq *) ioreq->io_Data;
  1255.     Disable();
  1256.     Remove(intreq);
  1257.     Enable();
  1258.     ReplyMsg(&intreq->io_Message);      /* Quick bit always cleared */
  1259.     ioreq->io_Error = 0;
  1260.     TermIO(ioreq);
  1261. }
  1262.  
  1263. void
  1264. DiskChangeHandler()
  1265. {
  1266.     auto UNIT     *unit;
  1267.     register struct IOStdReq *ioreq;
  1268.     register struct IOStdReq *next;
  1269. /* INDENT OFF */
  1270. #asm
  1271.     movem.l d2-d7/a2-a4,-(sp)
  1272.     move.l  a1,-4(a5)        ;unit
  1273. #endasm
  1274.     /* INDENT ON */
  1275.     unit->mu_DiskState = STATEF_UNKNOWN;
  1276.     unit->mu_ChangeNum++;
  1277.     unit->mu_SectorsPerTrack = MS_SPT;
  1278.     for (ioreq = (struct IOStdReq *) unit->mu_ChangeIntList.mlh_Head;
  1279.         next = (struct IOStdReq *) ioreq->io_Message.mn_Node.ln_Succ;
  1280.         ioreq = next) {
  1281.        Cause((struct Interrupt *) ioreq->io_Data);
  1282.     }
  1283. /* INDENT OFF */
  1284. #asm
  1285.     movem.l (sp)+,d2-d7/a2-a4
  1286. #endasm
  1287.     /* INDENT ON */
  1288. }
  1289.  
  1290. #ifndef READONLY
  1291.  
  1292. /*
  1293.  * Parts of the following code were written by Werner Guenther.
  1294.  * Used with permission.
  1295.  */
  1296.  
  1297. /* mu_TrackChanged is a flag. When a sector has changed it changes to 1 */
  1298.  
  1299. /*
  1300.  * InitWrite() has to be called once at startup. It allocates the space
  1301.  * for one raw track, and writes the low level stuff between sectors
  1302.  * (gaps, syncs etc.)
  1303.  */
  1304.  
  1305. int
  1306. InitWrite(dev)
  1307. DEV           *dev;
  1308. {
  1309.     if ((dev->md_Rawbuffer =
  1310.            AllocMem((long)RLEN+2, MEMF_CHIP | MEMF_PUBLIC)) == 0)
  1311.        return 0;
  1312.  
  1313.     return 1;
  1314. }
  1315.  
  1316. /*
  1317.  * FreeBuffer has to be called when msh: closes down, it just frees the
  1318.  * memory InitWrite has allocated
  1319.  */
  1320.  
  1321. void
  1322. FreeBuffer(dev)
  1323. DEV           *dev;
  1324. {
  1325.     if (dev->md_Rawbuffer) {    /* OIS */
  1326.        FreeMem(dev->md_Rawbuffer, (long) RLEN + 2);
  1327.     }
  1328. }
  1329.  
  1330. /*
  1331.  * This routine doesn't write to the disk, but updates the TrackBuffer to
  1332.  * respect the new sector. We have to be sure the TrackBuffer is filled
  1333.  * with the current Track. As GetSTS calls Internal_Update if the track
  1334.  * changes we don't have to bother about actually writing any data to the
  1335.  * disk. GetSTS has to be changed in the following way:
  1336.  *
  1337.  * if (track != mu_CurrentTrack || side != mu_CurrentSide) { Internal_Update(); for
  1338.  * (i = 0; i < MS_SPT; i++) ..... etc.
  1339.  */
  1340.  
  1341. void
  1342. CMD_Write(ioreq, unit)
  1343. register struct IOExtTD *ioreq;
  1344. UNIT          *unit;
  1345. {
  1346.     int            side;
  1347.     int            cylinder;
  1348.     int            sector;
  1349.     byte          *userbuf;
  1350.     long           length;
  1351.     long           offset;
  1352.     word           spt;
  1353.  
  1354.     debug(("CMD_Write "));
  1355.     userbuf = (byte *) ioreq->iotd_Req.io_Data;
  1356.     length = ioreq->iotd_Req.io_Length / MS_BPS;       /* Sector count */
  1357.     offset = ioreq->iotd_Req.io_Offset / MS_BPS;       /* Sector number */
  1358.     debug(("userbuf %08lx off %ld len %ld ", userbuf, offset, length));
  1359.  
  1360.     spt = unit->mu_SectorsPerTrack;
  1361.     cylinder = offset / spt;
  1362.     side = cylinder % MS_NSIDES;
  1363.     cylinder /= MS_NSIDES;
  1364.     sector = offset % spt;
  1365.     debug(("T=%d Si=%d Se=%d\n", cylinder, side, sector));
  1366.  
  1367.     ioreq->iotd_Req.io_Actual = 0;
  1368.  
  1369.     if (length <= 0 || CheckRequest(ioreq, unit))
  1370.        goto end;
  1371.  
  1372.     GetTrack(ioreq, side, cylinder);
  1373.     for (;;) {
  1374.        CopyMem(userbuf, unit->mu_TrackBuffer + MS_BPS * sector, (long) MS_BPS);
  1375.        unit->mu_TrackChanged = 1;
  1376.        unit->mu_SectorStatus[sector] = CRC_CHANGED;
  1377.  
  1378.        ioreq->iotd_Req.io_Actual += MS_BPS;
  1379.        if (--length <= 0)
  1380.            break;
  1381.        userbuf += MS_BPS;
  1382.        /*
  1383.         * Get next sequential sector/side/track
  1384.         */
  1385.        if (++sector >= spt) {
  1386.            sector = 0;
  1387.            if (++side >= MS_NSIDES) {
  1388.                side = 0;
  1389.                if (++cylinder >= unit->mu_NumCyls)
  1390.                    goto end;
  1391.            }
  1392.            GetTrack(ioreq, side, cylinder);
  1393.        }
  1394.     }
  1395.  
  1396.     if (length)
  1397.        ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
  1398.  
  1399. end:
  1400.     TermIO(ioreq);
  1401. }
  1402.  
  1403. /*
  1404.  * This is called by your GetSTS() routine if the Track has changed. It
  1405.  * writes the changes back to the disk (a whole track at a time). It has
  1406.  * to be called if your device gets a CLOSE instruction too.
  1407.  */
  1408.  
  1409. void
  1410. Internal_Update(ioreq, unit)
  1411. struct IOExtTD *ioreq;
  1412. register UNIT  *unit;
  1413. {
  1414.     debug(("Internal_Update "));
  1415.     /* did we have a changed sector at all      */
  1416.     if (unit->mu_TrackChanged != 0) {
  1417.        debug(("needs to write "));
  1418.  
  1419.        if (unit->mu_SectorsPerTrack > unit->mu_CurrentSectors)
  1420.            unit->mu_CurrentSectors = unit->mu_SectorsPerTrack;
  1421.  
  1422.        /*
  1423.         * Only recalculate the CRC on changed sectors. This way, a
  1424.         * sector with a bad CRC won't suddenly be ``repaired''.
  1425.         */
  1426.        {
  1427.            register int i;
  1428.  
  1429.            for (i = unit->mu_CurrentSectors - 1; i >= 0; i--) {
  1430.                if (unit->mu_SectorStatus[i] == CRC_CHANGED) {
  1431.                    unit->mu_CrcBuffer[i] = DataCRC(unit->mu_TrackBuffer + i * MS_BPS);
  1432.                    debug(("%d: %04x\n", i, unit->mu_CrcBuffer[i]));
  1433.                }
  1434.            }
  1435.        }
  1436.        {
  1437.            DEV            *dev;
  1438.            register struct IOExtTD *tdreq;
  1439.            word            SectorGap;
  1440.  
  1441.            dev = (DEV *) ioreq->iotd_Req.io_Device;
  1442.            tdreq = unit->mu_DiskIOReq;
  1443.            SectorGap = CalculateGapLength(unit->mu_CurrentSectors);
  1444.  
  1445.            ObtainSemaphore(&dev->md_HardwareUse);
  1446.            EncodeTrack(unit->mu_TrackBuffer, dev->md_Rawbuffer,
  1447.                        unit->mu_CrcBuffer,
  1448.                        unit->mu_CurrentTrack, unit->mu_CurrentSide,
  1449.                        SectorGap, unit->mu_CurrentSectors);
  1450.  
  1451.            TDMotorOn(tdreq);
  1452.            if (TDSeek(unit, ioreq, unit->mu_CurrentTrack)) {
  1453.                debug(("Seek error\n"));
  1454.                ioreq->iotd_Req.io_Error = TDERR_SeekError;
  1455.                goto end;
  1456.            }
  1457.            HardwareIO(dev, unit, DSKWRITE);
  1458.  
  1459.            ReleaseSemaphore(&dev->md_HardwareUse);
  1460.            unit->mu_TrackChanged = 0;
  1461.        }
  1462.     }
  1463. end:
  1464.     debug(("done\n"));
  1465. }
  1466.  
  1467. /*
  1468.  * TD_Format writes one or more whole tracks without reading them first.
  1469.  */
  1470.  
  1471. void
  1472. TD_Format(ioreq, unit)
  1473. register struct IOExtTD *ioreq;
  1474. UNIT          *unit;
  1475. {
  1476.     register struct IOExtTD *tdreq = unit->mu_DiskIOReq;
  1477.     DEV           *dev;
  1478.     short          side;
  1479.     int            cylinder;
  1480.     byte          *userbuf;
  1481.     int            length;
  1482.     word           spt;
  1483.     word           gaplen;
  1484.  
  1485.     debug(("CMD_Format "));
  1486.  
  1487.     if (CheckRequest(ioreq, unit))
  1488.        goto end;
  1489.  
  1490.     userbuf = (byte *) ioreq->iotd_Req.io_Data;
  1491.     length = ioreq->iotd_Req.io_Length / MS_BPS;           /* Sector count */
  1492.     cylinder = ioreq->iotd_Req.io_Offset / MS_BPS;         /* Sector number */
  1493.     /*
  1494.      * Now try to guess the number of sectors the user wants per track.
  1495.      * 40 sectors is the first ambuiguous length.
  1496.      */
  1497.     if (length < 40) {
  1498.        if (length > 0) {
  1499.            for (spt = 8; spt <= MS_SPT_MAX; spt++) {
  1500.                if ((length % spt) == 0)
  1501.                    goto found_spt;
  1502.            }
  1503.        }
  1504.        /*
  1505.         * Not 8, 16, 24, 32, 9, 18, 27, 36, 10, 20, or 30? That is an error.
  1506.         */
  1507.        ioreq->iotd_Req.io_Error = IOERR_BADLENGTH;
  1508.        goto end;
  1509.     } else  /* assume previous number */
  1510.        spt = unit->mu_SectorsPerTrack;
  1511.  
  1512. found_spt:
  1513.     gaplen = CalculateGapLength(spt);
  1514.  
  1515.     /*
  1516.      * Assume the whole disk will have this layout.
  1517.      */
  1518.     unit->mu_SectorsPerTrack = spt;
  1519.  
  1520.     length /= spt;
  1521.     cylinder /= spt;
  1522.  
  1523.     side = cylinder % MS_NSIDES;
  1524.     cylinder /= MS_NSIDES;
  1525.     debug(("userbuf %08lx cylinder %d len %d\n", userbuf, cylinder, length));
  1526.  
  1527.     ioreq->iotd_Req.io_Actual = 0;
  1528.  
  1529.     /*
  1530.      * Write out the current track if we are not going to overwrite it.
  1531.      * After the format operation, the buffer is invalidated.
  1532.      */
  1533.     if (cylinder <= unit->mu_CurrentTrack &&
  1534.        unit->mu_CurrentTrack < cylinder + length)
  1535.        Internal_Update(ioreq, unit);
  1536.  
  1537.     dev = (DEV *) ioreq->iotd_Req.io_Device;
  1538.  
  1539.     while (length > 0) {
  1540.        {
  1541.            register int i;
  1542.  
  1543.            for (i = spt - 1; i >= 0; i--) {
  1544.                unit->mu_CrcBuffer[i] = DataCRC(userbuf + i * MS_BPS);
  1545.                debug(("%d: %04x\n", i, unit->mu_CrcBuffer[i]));
  1546.            }
  1547.        }
  1548.        ObtainSemaphore(&dev->md_HardwareUse);
  1549.        EncodeTrack(userbuf, dev->md_Rawbuffer, unit->mu_CrcBuffer,
  1550.                    cylinder, side,
  1551.                    gaplen, spt);
  1552.  
  1553.        TDMotorOn(tdreq);
  1554.        if (TDSeek(unit, ioreq, cylinder)) {
  1555.            debug(("Seek error\n"));
  1556.            ioreq->iotd_Req.io_Error = IOERR_BADLENGTH;
  1557.            break;
  1558.        }
  1559.        unit->mu_CurrentSide = side;
  1560.        HardwareIO(dev, unit, DSKWRITE);
  1561.  
  1562.        ReleaseSemaphore(&dev->md_HardwareUse);
  1563.  
  1564.        length--;
  1565.        userbuf += MS_BPS * spt;
  1566.        ioreq->iotd_Req.io_Actual += MS_BPS * spt;
  1567.  
  1568.        if (++side >= MS_NSIDES) {
  1569.            side = 0;
  1570.            if (++cylinder >= unit->mu_NumCyls)
  1571.                goto end;
  1572.        }
  1573.     }
  1574. end:
  1575.     unit->mu_CurrentSide = -1;
  1576.     TermIO(ioreq);
  1577. }
  1578. /* INDENT OFF */
  1579.  
  1580. #asm
  1581.  
  1582. ; we need a buffer for the Sector-ID field to calculate its checksum
  1583. ;SectorHeader:
  1584. ;       dc.b    0                 ; track
  1585. ;       dc.b    0                 ; side
  1586. ;       dc.b    0                 ; sector
  1587. ;       dc.b    2                 ; length (2=512 bytes)
  1588. ;       dc.w    0                 ; CRC
  1589.  
  1590.        public _EncodeTrack
  1591.  
  1592. ;   EncodeTrack(TrackBuffer, Rawbuffer, Crcs, Track, Side, GapLen, NumSecs)
  1593. ;              4            4          4     2      2     2       2
  1594.  
  1595. _EncodeTrack:
  1596.        movem.l d2-d7/a2-a6,-(sp)  ; save registers
  1597.  
  1598. fp     set     (4*(6+5))+4        ; 4 for return address
  1599. trackbf set    0
  1600. rawbf  set     4
  1601. crcs   set     8
  1602. track  set     12
  1603. side   set     14
  1604. gaplen set     16
  1605. numsecs set    18
  1606.  
  1607. ; a0   ptr in encoded data (also putmfmbyte)
  1608. ; a2   ptr to mfm encoding table (putmfmbyte)
  1609. ; a3   ptr to data to be crc'd (HeaderCRC)
  1610. ; a4   ptr to table with calculated CRC's
  1611. ; a5   ptr to unencoded data
  1612.  
  1613. ; d0   byte to be encoded (putmfmbyte)
  1614. ; d1   trashed by putmfmbyte
  1615. ; d3   used by putmfmbyte
  1616. ; d5   sector number
  1617. ; d6   general counter of byte spans
  1618. ; d7   sector countdown
  1619.  
  1620.        sub.w   #2,fp+gaplen(sp)   ; gap length between sectors
  1621.        move.l  fp+rawbf(sp),a0    ; pointer to mfmencoded buffer
  1622.        move.l  fp+crcs(sp),a4     ; pointer to precalculated CRCs
  1623.        move.l  fp+trackbf(sp),a5  ; pointer to unencoded data
  1624.        lea     _MfmEncode,a2      ; pointer to MFM lookup table
  1625.  
  1626.        move.w  #$9254,d0          ; a track starts with a gap
  1627.        moveq   #INDEXGAP-1,d6     ; (60 * $4e)
  1628. ingl   move.w  d0,(a0)+           ; mfmencoded = $9254
  1629.        dbf     d6,ingl
  1630.  
  1631.        lea     -6(sp),sp          ; Reserve room for SectorHeader
  1632. fp     set     fp+6
  1633.        move.w  fp+numsecs(sp),d7  ; number of sectors to encode
  1634.        subq.w  #1,d7              ; minus 1 for dbra
  1635.        moveq   #0,d5              ; start with first sector
  1636.  
  1637. secloop:
  1638.        move.w  #$aaaa,d0          ; a sector starts with a gap containing
  1639.        moveq   #IDGAP2-1,d6       ; 12 * 0 (mfm = $aaaa)
  1640. id2gl  move.w  d0,(a0)+
  1641.        dbf     d6,id2gl
  1642.  
  1643.        move.w  #SYNC,d0           ; The ID field begins here, starting
  1644.        move.w  d0,(a0)+           ; with 3 syncs (3 * $a1) with a missing
  1645.        move.w  d0,(a0)+           ; clock bit
  1646.        move.w  d0,(a0)+
  1647.  
  1648.        move.w  #$5554,(a0)+       ; ID-Address mark ($fe)
  1649.  
  1650.        move.l  sp,a3              ; pointer to Sector-ID buffer
  1651.  
  1652.        moveq   #$5554&1,d3        ; preload d3 for the putmfmbyte routine
  1653.        move.b  fp+track+1(sp),0(a3)  ; insert current track number
  1654.        move.b  fp+side+1(sp),1(a3)   ; side number
  1655.        addq.w  #1,d5              ; sectors start with 1 instead of 0
  1656.        move.b  d5,2(a3)           ; sector number
  1657.        move.b  #MS_BPScode,3(a3)  ; sector length 512 bytes
  1658.        bsr     HeaderCRC          ; calculate checksum
  1659.        move.w  d0,IDDATA(a3)      ; put it past the data
  1660.  
  1661.        moveq   #IDDATA+IDCRC-1,d6 ; 6 bytes Sector-ID
  1662. sidl   move.b  (a3)+,d0           ; get one byte
  1663.        bsr     putmfmbyte         ; encode it
  1664.        dbf     d6,sidl            ; end of buffer ?
  1665.  
  1666.        moveq   #$4e,d0            ; recalculate the MFM value of the
  1667.        bsr     putmfmbyte         ; first gap byte
  1668.  
  1669.        moveq   #DATAGAP1-1-1,d6   ; GAP consisting of
  1670.        move.w  #$9254,d0          ; 22 * $4e
  1671. dg1l   move.w  d0,(a0)+
  1672.        dbf     d6,dg1l
  1673.  
  1674.        moveq   #DATAGAP2-1,d6     ; GAP consisting of
  1675.        move.w  #$aaaa,d0          ; 12 * 0 (mfm = $aaaa)
  1676. dg2l   move.w  d0,(a0)+
  1677.        dbf     d6,dg2l
  1678.  
  1679.        move.w  #SYNC,d0           ; Sector data
  1680.        move.w  d0,(a0)+           ; starts with 3 syncs
  1681.        move.w  d0,(a0)+
  1682.        move.w  d0,(a0)+
  1683.  
  1684.        move.w  #$5545,(a0)+       ; Data Address Mark ($fb)
  1685.  
  1686.        moveq   #$5545&1,d3        ; preload d3
  1687.        move    #MS_BPS-1,d6       ; a sector has 512 bytes
  1688. dblockl move.b (a5)+,d0           ; get one byte from the buffer
  1689.        bsr     putmfmbyte         ; encode it
  1690.        dbf     d6,dblockl         ; end of sector ?
  1691.  
  1692.        move.b  (a4)+,d0           ; get first byte of CRC
  1693.        bsr     putmfmbyte         ; encode it
  1694.        move.b  (a4)+,d0           ; get second byte
  1695.        bsr     putmfmbyte         ; encode it
  1696.  
  1697.        moveq   #$4e,d0            ; recalculate the MFM value of the
  1698.        bsr     putmfmbyte         ; first gap byte -> -1 in following loop
  1699.  
  1700. ;      moveq   #DATAGAP3-1-1,d6   ; sector ends with a gap
  1701.        move.w  fp+gaplen(sp),d6   ; sector ends with a gap, -1 for dbf
  1702.        move.w  #$9254,d0          ; 80 * $4e
  1703. dg3l   move.w  d0,(a0)+
  1704.        dbf     d6,dg3l
  1705.  
  1706.        dbf     d7,secloop         ; next sector. d5 has been incremented
  1707.  
  1708.        lea     6(sp),sp           ; Free room for SectorHeader
  1709. fp     set     fp-6
  1710.  
  1711.        move.l  fp+rawbf(sp),d6    ; pointer to mfmencoded buffer
  1712.        add.l   #WLEN,d6           ; end of encoded buffer
  1713.        move.l  a0,d0              ; (I really want to   sub.l a0,d6 )
  1714.        sub.l   d0,d6              ; length of the remains
  1715.        lsr.l   #1,d6              ; turn into words
  1716.  
  1717.        move.w  #$9254,d0          ; Fill the end of the track with $4e
  1718. endgl  move.w  d0,(a0)+           ; $9254 mfm encoded
  1719.        dbf     d6,endgl
  1720.  
  1721.        movem.l (sp)+,d2-d7/a2-a6
  1722.        rts
  1723.  
  1724. ; putmfmbyte encodes one byte (in D0) into MSDOS MFM format to the location
  1725. ; pointed by A0. D3 has to be preserved between calls !
  1726. ; A2 must contain the pointer to the encoding table.
  1727. ; Destroys D0, D1. Updates A0 and D3. Requires A0, D0, D3.
  1728.  
  1729. putmfmbyte
  1730.        moveq   #16-4,d1
  1731.        lsl.l   d1,d0           ; split the byte into two nibbles
  1732.        lsr.w   d1,d0           ; low nibble is in bits 0..15
  1733.                                ; high nibble in bits 16..31
  1734.        swap    d0              ; process high nibble first
  1735.        and.w   #$0f,d0         ; mask out unwanted bits
  1736.        move.b  0(a2,d0.w),d1   ; get mfmencoded nibble from table
  1737.        btst    #6,d1           ; we now have to work out if
  1738.        bne.s   1$              ; the high bit of the unencoded data
  1739.        btst    #0,d3           ; byte and the low bit of the last
  1740.        bne.s   1$              ; encoded data are both 0. if this is the
  1741.        bset    #7,d1           ; case the first clock bit has to be '1'
  1742. 1$     move.b  d1,(a0)+        ; write high (encoded) nibble
  1743.        swap    d0              ; process low nibble
  1744.        move.b  0(a2,d0.w),d3   ; ....same as above
  1745.        btst    #6,d3
  1746.        bne.s   2$
  1747.        btst    #0,d1
  1748.        bne.s   2$
  1749.        bset    #7,d3
  1750. 2$     move.b  d3,(a0)+
  1751.        rts
  1752.  
  1753. #endasm
  1754. #endif                         /* READONLY */
  1755. #asm
  1756.  
  1757. ; The CRC is computed not only over the actual data, but including
  1758. ; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb).
  1759. ; As we don't read or encode these fields into our buffers, we have to
  1760. ; preload the registers containing the CRC with the values they would have
  1761. ; after stepping over these fields.
  1762. ;
  1763. ; How CRCs "really" work:
  1764. ;
  1765. ; First, you should regard a bitstring as a series of coefficients of
  1766. ; polymomials. We calculate with these polynomials in modulo-2
  1767. ; arithmetic, in which both add and subtract are done the same as
  1768. ; exclusive-or. Now, we modify our data (a very long polynomial) in
  1769. ; such a way that it becomes divisible by the CCITT-standard 16-bit
  1770. ;               16   12   5
  1771. ; polynomial:  x  + x  + x + 1, represented by $11021. The easiest
  1772. ; way to do this would be to multiply (using proper arithmetic) our
  1773. ; datablock with $11021. So we have:
  1774. ;   data * $11021               =
  1775. ;   data * ($10000 + $1021)      =
  1776. ;   data * $10000 + data * $1021
  1777. ; The left part of this is simple: Just add two 0 bytes. But then
  1778. ; the right part (data $1021) remains difficult and even could have
  1779. ; a carry into the left part. The solution is to use a modified
  1780. ; multiplication, which has a result that is not correct, but with
  1781. ; a difference of any multiple of $11021. We then only need to keep
  1782. ; the 16 least significant bits of the result.
  1783. ;
  1784. ; The following algorithm does this for us:
  1785. ;
  1786. ;   unsigned char *data, c, crclo, crchi;
  1787. ;   while (not done) {
  1788. ;      c = *data++ + crchi;
  1789. ;      crchi = (@ c) >> 8 + crclo;
  1790. ;      crclo = @ c;
  1791. ;   }
  1792. ;
  1793. ; Remember, + is done with EOR, the @ operator is in two tables (high
  1794. ; and low byte separately), which is calculated as
  1795. ;
  1796. ;      $1021 * (c & $F0)
  1797. ;  xor $1021 * (c & $0F)
  1798. ;  xor $1021 * (c >> 4)         (* is regular multiplication)
  1799. ;
  1800. ;
  1801. ; Anyway, the end result is the same as the remainder of the division of
  1802. ; the data by $11021. I am afraid I need to study theory a bit more...
  1803.  
  1804.  
  1805. ; This is the entry to calculate the checksum for the sector-id field
  1806. ; requires:  a3 = pointer to the unencoded data
  1807. ; returns:   d0 = CRC
  1808.  
  1809. HeaderCRC:
  1810.        movem.l  d1-d3/a3-a5,-(sp) ; save registers
  1811.        move.w   #$b2,d0           ; preload registers
  1812.        moveq    #$30,d1           ; (CRC for $a1,$a1,$a1,$fb)
  1813.        moveq    #3,d3             ; calculate checksum for 4 bytes
  1814.        bra.s    getCRC            ; (=track,side,sector,sectorlength)
  1815.  
  1816. ; This is the entry to calculate the checksum for the data field
  1817. ; requires:  a3 = pointer to the unencoded data
  1818. ; returns:   d0 = CRC
  1819.  
  1820. DataCRC:
  1821.        movem.l  d1-d3/a3-a5,-(sp) ; save registers
  1822.        bra.s   DataCRC1
  1823.  
  1824. ; C entry point for DataCRC(byte *data)
  1825.  
  1826. _DataCRC:
  1827.        movem.l d1-d3/a3-a5,-(sp) ; save registers
  1828. fp     set     (4*(3+3))+4
  1829. data   set     0
  1830.        move.l  fp+data(sp),a3    ; get parameter
  1831. DataCRC1:
  1832.        move.w   #$e2,d0           ; preload the CRC registers
  1833.        move.w   #$95,d1           ; (CRC for $a1,$a1,$a1,$fe)
  1834.        move.w   #MS_BPS-1,d3      ; a sector 512 bytes
  1835.  
  1836. getCRC lea      CRCTable1,a4
  1837.        lea      CRCTable2,a5
  1838.        moveq    #0,d2
  1839.  
  1840. 1$     move.b   (a3)+,d2
  1841.        eor.b    d0,d2
  1842.        move.b   0(a4,d2.w),d0
  1843.        eor.b    d1,d0
  1844.        move.b   0(a5,d2.w),d1
  1845.        dbf      d3,1$
  1846.  
  1847.        lsl.w    #8,d0
  1848.        move.b   d1,d0
  1849.        movem.l  (sp)+,d1-d3/a3-a5
  1850.        rts
  1851.  
  1852.  
  1853. CRCTable1:
  1854.        dc.b $00,$10,$20,$30,$40,$50,$60,$70,$81,$91,$a1,$b1,$c1,$d1,$e1,$f1
  1855.        dc.b $12,$02,$32,$22,$52,$42,$72,$62,$93,$83,$b3,$a3,$d3,$c3,$f3,$e3
  1856.        dc.b $24,$34,$04,$14,$64,$74,$44,$54,$a5,$b5,$85,$95,$e5,$f5,$c5,$d5
  1857.        dc.b $36,$26,$16,$06,$76,$66,$56,$46,$b7,$a7,$97,$87,$f7,$e7,$d7,$c7
  1858.        dc.b $48,$58,$68,$78,$08,$18,$28,$38,$c9,$d9,$e9,$f9,$89,$99,$a9,$b9
  1859.        dc.b $5a,$4a,$7a,$6a,$1a,$0a,$3a,$2a,$db,$cb,$fb,$eb,$9b,$8b,$bb,$ab
  1860.        dc.b $6c,$7c,$4c,$5c,$2c,$3c,$0c,$1c,$ed,$fd,$cd,$dd,$ad,$bd,$8d,$9d
  1861.        dc.b $7e,$6e,$5e,$4e,$3e,$2e,$1e,$0e,$ff,$ef,$df,$cf,$bf,$af,$9f,$8f
  1862.        dc.b $91,$81,$b1,$a1,$d1,$c1,$f1,$e1,$10,$00,$30,$20,$50,$40,$70,$60
  1863.        dc.b $83,$93,$a3,$b3,$c3,$d3,$e3,$f3,$02,$12,$22,$32,$42,$52,$62,$72
  1864.        dc.b $b5,$a5,$95,$85,$f5,$e5,$d5,$c5,$34,$24,$14,$04,$74,$64,$54,$44
  1865.        dc.b $a7,$b7,$87,$97,$e7,$f7,$c7,$d7,$26,$36,$06,$16,$66,$76,$46,$56
  1866.        dc.b $d9,$c9,$f9,$e9,$99,$89,$b9,$a9,$58,$48,$78,$68,$18,$08,$38,$28
  1867.        dc.b $cb,$db,$eb,$fb,$8b,$9b,$ab,$bb,$4a,$5a,$6a,$7a,$0a,$1a,$2a,$3a
  1868.        dc.b $fd,$ed,$dd,$cd,$bd,$ad,$9d,$8d,$7c,$6c,$5c,$4c,$3c,$2c,$1c,$0c
  1869.        dc.b $ef,$ff,$cf,$df,$af,$bf,$8f,$9f,$6e,$7e,$4e,$5e,$2e,$3e,$0e,$1e
  1870.  
  1871. CRCTable2:
  1872.        dc.b $00,$21,$42,$63,$84,$a5,$c6,$e7,$08,$29,$4a,$6b,$8c,$ad,$ce,$ef
  1873.        dc.b $31,$10,$73,$52,$b5,$94,$f7,$d6,$39,$18,$7b,$5a,$bd,$9c,$ff,$de
  1874.        dc.b $62,$43,$20,$01,$e6,$c7,$a4,$85,$6a,$4b,$28,$09,$ee,$cf,$ac,$8d
  1875.        dc.b $53,$72,$11,$30,$d7,$f6,$95,$b4,$5b,$7a,$19,$38,$df,$fe,$9d,$bc
  1876.        dc.b $c4,$e5,$86,$a7,$40,$61,$02,$23,$cc,$ed,$8e,$af,$48,$69,$0a,$2b
  1877.        dc.b $f5,$d4,$b7,$96,$71,$50,$33,$12,$fd,$dc,$bf,$9e,$79,$58,$3b,$1a
  1878.        dc.b $a6,$87,$e4,$c5,$22,$03,$60,$41,$ae,$8f,$ec,$cd,$2a,$0b,$68,$49
  1879.        dc.b $97,$b6,$d5,$f4,$13,$32,$51,$70,$9f,$be,$dd,$fc,$1b,$3a,$59,$78
  1880.        dc.b $88,$a9,$ca,$eb,$0c,$2d,$4e,$6f,$80,$a1,$c2,$e3,$04,$25,$46,$67
  1881.        dc.b $b9,$98,$fb,$da,$3d,$1c,$7f,$5e,$b1,$90,$f3,$d2,$35,$14,$77,$56
  1882.        dc.b $ea,$cb,$a8,$89,$6e,$4f,$2c,$0d,$e2,$c3,$a0,$81,$66,$47,$24,$05
  1883.        dc.b $db,$fa,$99,$b8,$5f,$7e,$1d,$3c,$d3,$f2,$91,$b0,$57,$76,$15,$34
  1884.        dc.b $4c,$6d,$0e,$2f,$c8,$e9,$8a,$ab,$44,$65,$06,$27,$c0,$e1,$82,$a3
  1885.        dc.b $7d,$5c,$3f,$1e,$f9,$d8,$bb,$9a,$75,$54,$37,$16,$f1,$d0,$b3,$92
  1886.        dc.b $2e,$0f,$6c,$4d,$aa,$8b,$e8,$c9,$26,$07,$64,$45,$a2,$83,$e0,$c1
  1887.        dc.b $1f,$3e,$5d,$7c,$9b,$ba,$d9,$f8,$17,$36,$55,$74,$93,$b2,$d1,$f0
  1888.  
  1889. #endasm
  1890.  
  1891. /* INDENT ON */
  1892.